#include "ArcBall.h"

ArcBall::ArcBall(void)
{
	Reset() ;
	m_vDownPt = D3DXVECTOR3( 0, 0, 0 ) ;
	m_oldPt	= D3DXVECTOR3( 0, 0, 0 ) ;
	m_vCurrentPt = D3DXVECTOR3( 0, 0, 0 ) ;

	RECT rc ;
	GetClientRect(GetForegroundWindow(), &rc) ;
	SetWindow(rc.right, rc.bottom) ;
}

ArcBall::~ArcBall(void)
{
}

void ArcBall::Reset()
{
	D3DXQuaternionIdentity( &m_qDown );
	D3DXQuaternionIdentity( &m_qNow );
	D3DXQuaternionIdentity( &m_increament ) ;
	D3DXMatrixIdentity( &m_mRotation );
	m_bDrag = FALSE;
	m_fRadius = 1.0f;
}

void ArcBall::OnBegin(int nX, int nY)
{
	if( nX >= m_offset.x && 
		nX <= m_offset.x + m_nWidth &&
		nY >= m_offset.y &&
		nY < m_offset.y + m_nHeight )
	{
		m_bDrag = true ; // begin drag state
		m_qDown = m_qNow ;
		m_vDownPt = ScreenToVector((float)nX, (float)nY) ;
		m_oldPt = m_vDownPt ;
	}
}

void ArcBall::OnMove(int nX, int nY)
{
	if(m_bDrag)
	{
		m_vCurrentPt = ScreenToVector((float)nX, (float)nY) ;
		m_increament = QuatFromBallPoints( m_oldPt, m_vCurrentPt ) ;
		m_qNow = m_qDown * QuatFromBallPoints( m_vDownPt, m_vCurrentPt ) ;
		m_oldPt = m_vCurrentPt ;
	}
}

void ArcBall::OnEnd()
{
	m_bDrag = false ;
}

void ArcBall::SetWindow( int nWidth, int nHeight )
{
	 m_nWidth = nWidth; 
	 m_nHeight = nHeight; 
	 m_vCenter = D3DXVECTOR2(m_nWidth / 2.0f, m_nHeight / 2.0f);
}

D3DXQUATERNION ArcBall::GetRotationQuat()
{
	return m_qNow ;
}

D3DXQUATERNION ArcBall::GetRotationQuatIncreament()
{
	return m_increament ;
}

const D3DXMATRIX* ArcBall::GetRotationMatrix()
{
	return D3DXMatrixRotationQuaternion(&m_mRotation, &m_qNow) ;
}

D3DXQUATERNION ArcBall::QuatFromBallPoints(const D3DXVECTOR3 &vFrom, const D3DXVECTOR3 &vTo)
{
	D3DXVECTOR3 vPart;
	float fDot = D3DXVec3Dot( &vFrom, &vTo );	
	D3DXVec3Cross( &vPart, &vFrom, &vTo );		

	return D3DXQUATERNION( vPart.x, vPart.y, vPart.z, fDot );
}

D3DXVECTOR3 ArcBall::ScreenToVector(float fScreenPtX, float fScreenPtY)
{
	// Screen point to arcball point
	FLOAT x = -( fScreenPtX - m_nWidth / 2 ) / ( m_fRadius * m_nWidth / 2 );
	FLOAT y = ( fScreenPtY  - m_nHeight / 2 ) / ( m_fRadius * m_nHeight / 2 );

	FLOAT z = 0.0f;
	FLOAT mag = x * x + y * y;

	// normalize
	if( mag > 1.0f )
	{
		FLOAT scale = 1.0f / sqrtf( mag );
		x *= scale;
		y *= scale;
	}
	else
		z = sqrtf( 1.0f - mag );

	// Return vector
	return D3DXVECTOR3( x, y, z );
}

LRESULT ArcBall::HandleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	int iMouseX = ( short )LOWORD( lParam );
	int iMouseY = ( short )HIWORD( lParam );

	switch( uMsg )
	{
	case WM_LBUTTONDOWN:
		SetCapture(hWnd) ;
		OnBegin(iMouseX, iMouseY) ;
		return TRUE ;

	case WM_MOUSEMOVE:
		OnMove(iMouseX, iMouseY) ;
		return TRUE ;

	case WM_RBUTTONDOWN:
		ReleaseCapture() ;
		OnEnd() ;
		return TRUE ;

	case WM_CAPTURECHANGED:
		ReleaseCapture() ;
		OnEnd() ;
		return TRUE ;
	}

	return FALSE ;
}	
